import scala.collection.mutable.Stack
import scala.swing._
import scala.swing.event._
import scala.Math

import java.awt.{Color}
import java.awt.geom._

val debug = false
def debugPrint(msg: Any) {
	if (debug) println(msg)
}

class Turtle(var x: Int, var y: Int, var angle: Float)
case class Rule(search: String, replace:List[String])

def applyRules(rules: List[Rule], axiom: List[String], times: Int) = {
    def applyRulesH(theorem: List[String], count: Int): List[String] = {
        if (count > times) theorem
        else {
            val appliedOnce = (for (item <- theorem) yield {
                val indOfRule = rules.indexWhere(_.search == item)
                if (indOfRule != -1) rules(indOfRule).replace
                else List(item)
            }).reduceLeft(_ ::: _).map(item => if (item == "|" || item == "/") "" + count + item else item)
            debugPrint("Hello?")
            applyRulesH(appliedOnce, count + 1)
        }
    }
    applyRulesH(axiom, 1)
}

def degree2rad(deg: Float): Double = {
    deg / 180 * Math.Pi
}

def turtleMove(turtle: Turtle, dist: Double): (Int, Int) = {
    ((turtle.x + dist * Math.cos(degree2rad(turtle.angle) )).toInt,
        (turtle.y + dist * Math.sin(degree2rad(turtle.angle) )).toInt )
}

class TurtleGUI {
val sizeTG = new Dimension(1000, 1000)
val dsField    = new TextField("0.65")
val angleField = new TextField("90.0")
val (xField, yField) = (new TextField("400"), new TextField("400") )
val distField = new TextField("100")
val parameters = new GridPanel(3, 4) {
    contents += new Label("ds")
    contents += dsField
    contents += new Label("angle")
    contents += angleField

    contents += new Label("init x")
    contents += xField
    contents += new Label("init y")
    contents += yField

    contents += new Label("Travel Distance")
    contents += distField
}
val prodHowTo = new GridPanel(9,1) {
    contents += new Label("Production Rules")
    contents += new Label("F : Go forward, draw")
    contents += new Label("G : Go forward, nodraw")
    contents += new Label("+ : Turn right (degrees)")
    contents += new Label("- : Turn left  (degrees)")
    contents += new Label("| : Go forward, draw (non-expandable)")
    contents += new Label("/ : Go forward, nodraw (non-expandable)")
    contents += new Label("[ : Save turtle")
    contents += new Label("] : Restore turtle")
}
val (prodArea, axiomArea) = (new TextArea("F=| [ - F ] | [ + F ] [ - F ] F"), new TextArea("") )
val iterArea = new TextField("1")
var currTheorem = List[String]()

val graphicArea = new Panel {
    override def paint(g:Graphics2D) {
        val (xin, yin, dist, angle, ds) = (xField.text.toInt, yField.text.toInt, distField.text.toInt, angleField.text.toFloat, dsField.text.toDouble)
        val iters = iterArea.text.toInt

        g.setColor(Color.WHITE)
        g.fillRect(0, 0, sizeTG.width, sizeTG.height)
        g.setColor(Color.BLACK)

        var turtle = new Turtle(xin, yin, 270.0F)
        val tstack = new Stack[Turtle]()

        g.drawString(currTheorem.mkString("  "), 0, 30)

        if (!currTheorem.isEmpty) for (command <- currTheorem) {
            command.last match {
                case 'F' => {
                    val (newx, newy) = turtleMove(turtle, dist * Math.pow(ds, iters) )
                    g.drawLine(turtle.x, turtle.y, newx, newy)
                    turtle.x = newx; turtle.y = newy
                }
                case 'G' => {
                    val (newx, newy) = turtleMove(turtle, dist * Math.pow(ds, iters) )
                    turtle.x = newx; turtle.y = newy
                }
                case '+' => {
                    turtle.angle += {if (command dropRight(1) isEmpty)
                            angle
                        else
                            command.split("\\+")(0).toFloat
                    }
                }
                case '-' => {
                    turtle.angle -=  {if (command dropRight(1) isEmpty) 
                            angle
                        else
                            command dropRight(1) toFloat
                    }
                }
                case '[' => {
                    tstack.push(new Turtle(turtle.x, turtle.y, turtle.angle) )
                }
                case ']' => {
                    debugPrint(tstack nonEmpty)
                    if (tstack nonEmpty) { 
                        turtle = tstack.pop()
                    }
                }
                case '|' => {
                    val power = {if (command dropRight(1) isEmpty) 
                            0
                        else
                            command.dropRight(1).toInt
                    }
                    val (newx, newy) = turtleMove(turtle, dist * Math.pow(ds, power) )
                    g.drawLine(turtle.x, turtle.y, newx, newy)
                    turtle.x = newx; turtle.y = newy
                }
                case '/' => {
                     val power = {if (command dropRight(1) isEmpty) 
                            1
                        else
                            command.split("/")(0).toInt + 1
                    }
                    val (newx, newy) = turtleMove(turtle, dist * Math.pow(ds, power) )
                    turtle.x = newx; turtle.y = newy
                }
                case _ => {}
            }
        }

 //       g.drawString(currTheorem.mkString(" "), 0, 20)
    }
}

def allFieldsValid = {
    try {
        List(dsField, angleField, xField, yField, distField) map (_.text.toDouble)
        prodArea.text.split("\n").map(rule => {val temp = rule.split("=");  Rule(temp(0), temp(1).split(" ").toList)})
        iterArea.text.toInt
        true
    } catch {
        case _ => false
    }
}

val runButton = Button("Apply and run") {
    if (allFieldsValid) {
        val iters = iterArea.text.toInt
        val rules = prodArea.text.split("\n").map(rule => {val temp = rule.split("="); Rule(temp(0), temp(1).split(" ").toList)} ).toList
        currTheorem = applyRules(rules, axiomArea.text.split(" ").toList, iters)
        graphicArea.repaint()
    }
}
val frame = new MainFrame {
	title = "Turtle Graphics"
	contents = new BorderPanel {
		import BorderPanel.Position._
        layout += parameters -> North
		layout += new GridPanel(2, 1) {
			contents += prodHowTo
			contents += prodArea
		} -> West
		layout += new GridPanel(2, 1) {
			contents += new Label("Axioms")
			contents += axiomArea
		} -> East
		layout += graphicArea -> Center
		layout += new GridPanel(1, 2) {
			contents += runButton
			contents += iterArea
		} -> South
	}
    size = sizeTG
    visible = true
}

}

val tgui = new TurtleGUI()


//now testing for automatic authentication
